/***********************************************************************
 * $Id : Socket.cpp    2011/11/14
 *
 *       swhors@naver.com
 * $hist : 2011/11/14 14/37 first
 ***********************************************************************/
 
#include <iostream>
#include <memory>

#include <stdio.h>
#include <memory.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netdb.h>

#include <unistd.h>

#include "constant.h"

#include "Socket.h"

using namespace std;

CSocket::CSocket()
{
   meSocketType = e_socket_tcp; 
   meConnectDir  = e_socket_listen;
   memset(mListenAddr, 0, sizeof(char) * SOCK_ADDR_LEN);
   memset(mConnectAddr, 0, sizeof(char) * SOCK_ADDR_LEN);
   mListenPort = 0;
   mConnectPort = 0;
}

CSocket::~CSocket()
{
}

void CSocket::finalize()
{
   Close();
}

void CSocket::Close()
{
   if ( mSocketfd > 0 )
      close( mSocketfd);
}

int CSocket::initialize(ESOCKETTYPE aeSockType, ESOCKETDIR aeConDir)
{
   meSocketType = aeSockType;
   meConnectDir = aeConDir;
   return initialize();
}
int CSocket::initialize()
{
   ERETURNVAL sRetVal = e_ret_false; 
   int        iState  = 0;
   
   sRetVal = Create();

   if ( sRetVal == e_ret_false )
   {
      goto err_initialize;
   }
   else
   {
      /* do nothing */
   }
   
   iState  = 1;
   
   return e_ret_true;

err_initialize:

   switch(iState)
   {
   case 2:
   case 1:
      Close();
   case 0:
   default:
      break;
   }
   return sRetVal;
}

ERETURNVAL CSocket::Listen()
{
   ERETURNVAL          sRetVal = e_ret_false;
   int                 sRet    = 0;
   
   if ( mSocketfd <= 0 )
   {
      sRetVal = e_ret_false;
   }
   else
   {
      /* do nothing */
   }
   
   sRet = listen(mSocketfd, 5);
   
   if ( sRet == 0 )
   {
      sRetVal = e_ret_true;
   }
   else
   {
      goto err_listen;
   }
   
   return sRetVal;
err_listen:
   return sRetVal;
}

ERETURNVAL CSocket::Bind()
{
   ERETURNVAL          sRetVal = e_ret_false;
   int                 sRet    = 0;
   int                 sVal    = 1;
   struct sockaddr_in  sAddr; 
   int                 sState  = 0;

   memset( &sAddr, 0, sizeof(struct sockaddr_in) );
   
   sRet = setsockopt( mSocketfd,
                      SOL_SOCKET,
                      SO_REUSEADDR,
                      (void *)&sVal,
                      sizeof( int ) );
   if ( sRet == -1 )
   {
      goto err_bind;
   }
   else
   {
      /* do nothing */
   }
   
   sState = 1; 
   
   sAddr.sin_family = AF_INET;
   if ( mListenPort <= 0 )
   {
      goto err_bind;
   }
   else
   {
      sAddr.sin_port = htons(mListenPort);
   }
   
   sAddr.sin_addr.s_addr = INADDR_ANY;
   
   
   sRet = bind( mSocketfd,
                ( struct sockaddr *) & sAddr, 
                sizeof( sAddr) );
   if ( sRet != 0 )
   {
      goto err_bind;
   }
   else
   {
      sRetVal = e_ret_true;
   }
   sState = 2; 

   return sRetVal;

err_bind:
   
   switch ( sState )
   {
   case 1:
      /* do nothing*/
   case 0:
      /* do nothing*/
   default:
      /* do nothing*/
      break;
   }

   return sRetVal;
}

ERETURNVAL CSocket::Create()
{
   ERETURNVAL sRetVal = e_ret_false; 
   
   
   int sSockType = (meSocketType==e_socket_tcp?SOCK_STREAM:SOCK_DGRAM);
   
   mSocketfd = socket( AF_INET, sSockType, 0);
   
   
   
   if ( mSocketfd <= 0 )
   {
      goto create_fail;
   }
   
   return e_ret_true;
   
create_fail:
   return sRetVal;
}

int CSocket::getSocketFd()
{
   return mSocketfd;
}

void CSocket::setListenInfo( char *aAddrStr, int aPortInt )
{
   snprintf( mListenAddr,
             SOCK_ADDR_LEN,
             "%s",
             aAddrStr );
   mListenPort = aPortInt;
}

void CSocket::setConnectInfo( char *aAddrStr, int aPortInt )
{
   snprintf( mConnectAddr,
             SOCK_ADDR_LEN,
             "%s",
             aAddrStr );
   mConnectPort = aPortInt;
}

int CSocket::getListenInfo( char *aAddrStr, int aBufLen, int *aPortInt )
{
   ERETURNVAL sRetVal = e_ret_true; 
   
   if ( aAddrStr != NULL && aBufLen >= SOCK_ADDR_LEN && aPortInt != NULL )
   {
      snprintf( aAddrStr,
                aBufLen,
                "%s",
                mListenAddr);
      *aPortInt = mListenPort;
   }
   else
   {
      sRetVal = e_ret_false;
   }
   
   return sRetVal;
}

int CSocket::getConnectInfo( char *aAddrStr, int aBufLen, int *aPortInt )
{
   ERETURNVAL sRetVal = e_ret_false; 
   
   if ( aAddrStr != NULL && aBufLen >= SOCK_ADDR_LEN && aPortInt != NULL )
   {
      snprintf( aAddrStr,
                aBufLen,
                "%s",
                mListenAddr);
      *aPortInt = mListenPort;
   }
   else
   {
      sRetVal = e_ret_false;
   }
   
   return sRetVal;
}

